
  {PARSE.PA2}

  {Parse Sentence}

  PROCEDURE Parse(VAR sentence : s;
                  VAR verb, noun, prep, object_word : words;
                  VAR syntax_error : Boolean);

  CONST
    max_words = 12;               {max words allowed in sentence}
  TYPE
    wordlists = ARRAY[1..max_words] OF words;
  VAR
    i : Integer;                  {index to s}
    k : Integer;                  {a spare index variable}
    l : Integer;                  {length of s}
    num_words : Integer;
    w : wordlists;
    OK_Verb, OK_Noun : Boolean;
    adj, NextPrep, NextObject : words;
    Ch : Char;
  LABEL Exit;

    { Close_Up }
    {Remove the specified word from the}
    {array of words. }
    PROCEDURE close_up(VAR w : wordlists;
                       VAR num_words : Integer;
                       N : Integer);
    VAR i : Integer;
    BEGIN
      FOR i := N TO num_words-1 DO
        w[i] := w[i+1];
      w[num_words] := '';
      num_words := num_words-1;
    END;

    { Is_Extra }
    {Returns 'true' if the word is}
    {considered 'unimportant.' }
    FUNCTION Is_Extra(w : words) : Boolean;
    BEGIN
      IF (w = 'THE') OR (w = 'PLEASE') OR (w = 'NOW')
      OR (w = 'A') OR (w = 'AN') OR (w = 'ITS') OR (w = 'MY')
      THEN Is_Extra := True
      ELSE Is_Extra := False;
    END;                          {Is_Extra}

    { Verb_Needs_Prep }
    {Checks word against hard-coded list of verbs that need preps.}
    FUNCTION Verb_Needs_Prep(w : words) : Boolean;
    BEGIN
      Verb_Needs_Prep := ((w = 'FIRE')
                          OR (w = 'PUT')
                          OR (w = 'TELL')
                          OR (w = 'ASK')
                          OR (w = 'LOCK')
                          OR (w = 'UNLOCK'));
    END;                          {Verb_Needs_Prep}

    { Is_Valid_Noun }
    {Returns TRUE if noun or creature and FALSE otherwise}
    FUNCTION Is_Valid_Noun(w : words) : Boolean;
    VAR
      match : Boolean;
    BEGIN
      match := Is_Noun(w);
      IF NOT match THEN match := Is_Creature(w);
      Is_Valid_Noun := match;
    END;                          {Is_Valid_Noun}

    { Handle Duplicates in Room}
    PROCEDURE HandleDuplicates(NorO : Integer);
      {If NorO is 0 then gets NounNumber of duplicate otherwise get ObjectNumber}
    VAR Answer : s;
      PickedOne : Boolean;
      i : Integer;
      Dword : words;
    BEGIN
    END;                          {HandleDuplicates}

    { Massage_Noun }
    {Takes care of any adjective-noun pairs}
    {by replacing them with the noun alone.}
    PROCEDURE Massage_Noun(VAR w : wordlists;
                           VAR num_words : Integer;
                           P : Integer);
    VAR i : Integer;              {index to verbs array}
      match : Boolean;
    BEGIN
      adj := '';
      IF w[P+1] <> '' THEN
        BEGIN
          match := False;
          i := First_noun;
          WHILE (i <= MaxNoun) AND
          (NOT match) DO
            BEGIN
              IF N[i]^.Has_Synonyms
              THEN match := ((w[P] = N[i]^.adj) AND (POS(' '+w[P+1]+' ', N[i]^.synonyms) <> 0))
              ELSE match := ((w[P] = N[i]^.adj) AND (N[i]^.name = w[P+1]));
              IF match THEN
                BEGIN
                  adj := w[P];    {get adj for this noun -- if input}
                  close_up(w, num_words, P);
                END;
              i := i+1;
            END;                  {exits if match found}
          IF NOT match THEN
            BEGIN
              i := First_creature;
              WHILE (i <= MaxCreature) AND
              (NOT match) DO
                BEGIN             { MaxCreature > 0 }
                  IF M[i]^.Has_Synonyms
                  THEN match := ((w[P] = M[i]^.adj) AND (POS(' '+w[P+1]+' ', M[i]^.synonyms) <> 0))
                  ELSE match := ((w[P] = M[i]^.adj) AND (M[i]^.name = w[P+1]));
                  IF match THEN
                    BEGIN
                      adj := w[P]; {get adj for this noun -- if input}
                      close_up(w, num_words, P);
                    END;
                  i := i+1;
                END;              {exits if match found}
            END;                  {IF NOT Match}
        END;                      {IF w[p+1] <> ''}
    END;                          {Massage_Noun}

    { Massage_Verb }
    {Deals with compound verbs, by }
    {converting them to single word 'verbs.'}
    {Also handles SYNONYMS }
    PROCEDURE Massage_Verb(VAR w : wordlists;
                           VAR num_words : Integer);
    VAR i : Integer;
      FirstCut : words;
    BEGIN
      Original_Verb := ' ';       {to begin with}
      IF Length(w[1]) = 1 THEN
        BEGIN
          IF (w[1] = 'E') THEN w[1] := 'EAST';
          IF (w[1] = 'S') THEN w[1] := 'SOUTH';
          IF (w[1] = 'N') THEN w[1] := 'NORTH';
          IF (w[1] = 'W') THEN w[1] := 'WEST';
          IF (w[1] = 'U') THEN w[1] := 'UP';
          IF (w[1] = 'D') THEN w[1] := 'DOWN';
          IF (w[1] = 'L') THEN w[1] := 'LOOK';
          IF (w[1] = 'I') THEN w[1] := 'INVENTORY';
          IF (w[1] = 'H') THEN w[1] := 'HELP';
          IF (w[1] = 'Q') THEN w[1] := 'QUIT';
        END;
      IF Length(w[1]) = 2 THEN
        BEGIN
          IF (w[1] = 'EX') THEN w[1] := 'EXAMINE';
          IF (w[1] = 'NE') THEN w[1] := 'NORTHEAST';
          IF (w[1] = 'SE') THEN w[1] := 'SOUTHEAST';
          IF (w[1] = 'NW') THEN w[1] := 'NORTHWEST';
          IF (w[1] = 'SW') THEN w[1] := 'SOUTHWEST';
        END;
      IF (w[1] = 'EXT') THEN w[1] := 'EXTINGUISH';
      IF (w[1] = 'LEAVE') THEN w[1] := 'EXIT';
      IF (w[1] = 'PICK') AND (w[2] = 'UP')
      THEN BEGIN
        Original_Verb := 'PICK UP';
        w[1] := 'GET';
        close_up(w, num_words, 2);
      END
      ELSE IF (w[1] = 'TAKE') AND (w[2] = 'OFF')
      THEN BEGIN
        Original_Verb := 'TAKE OFF';
        w[1] := 'REMOVE';
        close_up(w, num_words, 2);
      END
      ELSE IF ((w[1] = 'TAKE') OR (w[1] = 'GET'))
      THEN BEGIN
        Original_Verb := w[1];
        w[1] := 'GET';
      END
      ELSE IF ((w[1] = 'THROW') OR (w[1] = 'CAST') OR (w[1] = 'DUMP'))
      THEN BEGIN
        Original_Verb := w[1];
        w[1] := 'THROW';
      END
      ELSE IF ((w[1] = 'SCREAM') OR (w[1] = 'YELL') OR (w[1] = 'SHOUT'))
      THEN BEGIN
        Original_Verb := w[1];
        w[1] := 'YELL';
      END
      ELSE IF ((w[1] = 'EXAMINE') OR (w[1] = 'INSPECT') OR (w[1] = 'CHECK'))
      THEN BEGIN
        Original_Verb := w[1];
        w[1] := 'EXAMINE';
      END
      ELSE IF ((w[1] = 'KILL') OR (w[1] = 'ATTACK') OR (w[1] = 'HIT') OR (w[1] = 'FIGHT'))
      THEN BEGIN
        Original_Verb := w[1];
        w[1] := 'ATTACK';
      END
      ELSE IF ((w[1] = 'PUSH') OR (w[1] = 'TOUCH'))
      THEN BEGIN
        Original_Verb := w[1];
        w[1] := 'PUSH';
      END
      ELSE IF ((w[1] = 'SHOOT') OR (w[1] = 'FIRE'))
      THEN BEGIN
        Original_Verb := w[1];
        w[1] := 'FIRE';
      END
      ELSE IF ((w[1] = 'CLOSE') OR (w[1] = 'SHUT'))
      THEN BEGIN
        Original_Verb := w[1];
        w[1] := 'CLOSE';
      END
      ELSE IF ((w[1] = 'LOOK') AND ((w[2] = 'AT') OR (w[2] = 'IN')))
      THEN BEGIN
        Original_Verb := 'LOOK AT';
        w[1] := 'EXAMINE';
        close_up(w, num_words, 2);
      END
      ELSE IF ((w[1] = 'GO') AND ((w[2] = 'IN') OR (w[2] = 'INTO')))
      THEN BEGIN
        Original_Verb := 'GO INTO';
        w[1] := 'ENTER';
        close_up(w, num_words, 2);
      END
      ELSE IF (w[1] = 'TALK') AND (w[2] = 'TO')
      THEN BEGIN
        Original_Verb := 'TALK TO';
        w[1] := 'TELL';
        close_up(w, num_words, 2);
      END
      ELSE IF (w[1] = 'TALK') AND (w[2] = 'WITH')
      THEN BEGIN
        Original_Verb := 'TALK WITH';
        w[1] := 'TELL';
        close_up(w, num_words, 2);
      END
      ELSE IF (w[1] = 'PUT') AND (w[2] = 'DOWN')
      THEN BEGIN
        Original_Verb := 'PUT DOWN';
        w[1] := 'DROP';
        close_up(w, num_words, 2);
      END
      ELSE IF (w[1] = 'PUT') AND (w[2] = 'ON')
      THEN BEGIN
        Original_Verb := 'PUT ON';
        w[1] := 'WEAR';
        close_up(w, num_words, 2);
      END
      ELSE IF (w[1] = 'PUT') AND (w[2] = 'OUT')
      THEN BEGIN
        Original_Verb := 'PUT OUT';
        w[1] := 'EXTINGUISH';
        close_up(w, num_words, 2);
      END
      ELSE IF ((w[1] = 'PUT') OR (w[1] = 'PLACE'))
      THEN BEGIN
        Original_Verb := w[1];
        w[1] := 'PUT';
      END;
      FirstCut := w[1];
      {Now do global synonyms -- they take precedent over normal verbs}
      FOR i := 1 TO Num_Verbs DO
        IF POS(' '+FirstCut+' ', SYN[i]) <> 0
        THEN BEGIN
          Original_Verb := FirstCut;
          w[1] := V[i];           {substitute "Global" synonyms}
        END;
      {Now do room synonyms -- they take precedent over global synonyms}
      IF Skip_A_Line THEN IF POS(' '+FirstCut+' ', Room[Current_room]^.Replacing_Words) <> 0
        THEN BEGIN
          Original_Verb := FirstCut;
          w[1] := Room[Current_room]^.Replaced_Word;
          {Swap for ROOM SYNONYMS}
        END;
      IF w[1] = 'CHANGE_LOCATION' THEN w[1] := 'CHANGE_LOCATIONS';
      IF Is_Verb(w[1]) THEN
        BEGIN
          OK_Verb := True;
          IF (Original_Verb = ' ') THEN Original_Verb := w[1];
        END
      ELSE Original_Verb := LastVerb;
      Normalize(Original_Verb);
    END;                          {Massage_Verb}

    PROCEDURE Make_Into_Words(VAR sentence : s; VAR w : wordlists; VAR num_words : Integer);
    VAR k : Integer;
    BEGIN
      FOR k := 1 TO max_words DO
        w[k] := '';               {initialize word_list to avoid run-time crash}
      l := Length(sentence);
      i := 1;
      num_words := 0;
      Skip_spaces(sentence, i, l);
      WHILE ((i <= l) AND (num_words < max_words)) DO
        BEGIN
          num_words := num_words+1; {we've found another word}
          REPEAT
            w[num_words] := w[num_words]+sentence[i];
            i := i+1;
            Ch := sentence[i];
          UNTIL ((Ch > 'z') OR ((Ch < '0') AND (Ch <> '-') AND (Ch <> Chr(39))) OR (i > l));
          Skip_spaces(sentence, i, l);
        END;                      {while more characters to process}
      IF ((num_words = max_words) AND (i <= l))
      THEN
        BEGIN
          WriteLn('Too many words in sentence');
          syntax_error := True;
        END;
      { The words in the sentence are now }
      { broken out into separate words in the }
      { array of words "w", where they can be }
      { examined individually. }
      {Now let's delete any obviously extraneous words}
      k := 1;
      REPEAT
        IF Is_Extra(w[k])         {if word adds no meaning to the}
        THEN close_up(w, num_words, k) {sentence, then eliminate it }
        ELSE k := k+1;
      UNTIL k > num_words;        {loop exit is guaranteed because every time}
      {either k increases or num_words decreases}
    END;                          {Make_Into_Words}

    PROCEDURE Scan_For_Prep_Object(LastPart : s; VAR NextPrep, NextObject : words;
                                   VAR syntax_error : Boolean);
    VAR wd : wordlists; num, P : Integer; Finished : Boolean;
    BEGIN
      Make_Into_Words(LastPart, wd, num);
      P := 0;
      Finished := False;
      NextPrep := '';
      NextObject := '';
      syntax_error := False;
      REPEAT                      {scan for next prep in last part of sentence}
        P := P+1;
        IF Is_Prep(wd[P]) THEN
          BEGIN                   {found it}
            NextPrep := wd[P];
            Finished := True;
          END;
      UNTIL Finished OR (P = num);
      Massage_Noun(wd, num, P+1); {check next word(s) for noun}
      IF Is_Valid_Noun(wd[P+1]) THEN NextObject := wd[P+1];
      IF (NextPrep = '') OR (NextObject = '') THEN syntax_error := True;
    END;                          {Scan_For_Prep_Object}


  BEGIN                           {Parse(sentence)}
    OK_Verb := False;
    OK_Noun := False;
    Duplicate := False;
    verb := '';
    noun := '';
    prep := '';
    object_word := '';
    noun_adj := '';
    object_adj := '';
    NounNumber := 0;
    ObjectNumber := 0;
    syntax_error := False;
    IF Skip_A_Line THEN WriteLn(' ');
    IF sentence = '' THEN
      BEGIN
        WriteLn('Eh?');
        syntax_error := True;
      END
    ELSE
      BEGIN
        syntax_error := False;
        Make_Into_Words(sentence, w, num_words);
        Massage_Verb(w, num_words); {Now that unnecessary words are removed}
        {let's massage the first word in case it's a compound verb }
        IF NOT OK_Verb
        THEN IF (DoingCompoundCommands OR PreviousCompoundCommands) THEN
            BEGIN
              Massage_Noun(w, num_words, 1); {Is it another noun?}
              noun_adj := adj;
              {now, check to see if it's a recognized noun}
              OK_Noun := Is_Valid_Noun(w[1]);
              IF OK_Noun THEN
                IF NOT Verb_Needs_Prep(LastVerb) THEN
                  BEGIN           {simple verbs, like GET, DROP, etc}
                    FOR i := num_words DOWNTO 1 DO w[i+1] := w[i];
                    w[1] := LastVerb; {move last verb to w[1]}
                    num_words := num_words+1;
                  END
                ELSE
                  BEGIN           {Complex Verbs like SHOOT MONSTER WITH LASER}
                    Scan_For_Prep_Object(LastPart, NextPrep, NextObject, syntax_error);
                    IF syntax_error THEN
                      WriteLn('You need a preposition and object whenever you try to "', LastVerb, ' ', w[1]
                              , '"!')
                    ELSE BEGIN
                      w[2] := w[1];
                      w[1] := LastVerb;
                      w[3] := NextPrep;
                      w[4] := NextObject;
                    END
                  END
              ELSE                {NOT OK_Noun}
                BEGIN
                  WriteLn('I don''t understand ', w[1], ' as either a verb or a noun.');
                  syntax_error := True;
                END;
            END                   {DoingCompoundCommands}
          ELSE BEGIN
            WriteLn('I don''t understand ', w[1], ' as a verb.');
            syntax_error := True;
          END
          { If the second word is '' or shouldn't exist, let's exit.}
        ELSE IF NOT((num_words = 1) OR (Is_Direction(w[1])))
        THEN                      {check the word/phrase following the verb}
          BEGIN
            {first, massage nouns in case they're compound nouns}
            Massage_Noun(w, num_words, 2);
            noun_adj := adj;
            {now, check to see if it's a recognized noun}
            OK_Noun := Is_Valid_Noun(w[2]);
            IF NOT OK_Noun
            THEN IF Is_Prep(w[2])
              THEN
                BEGIN
                  Massage_Noun(w, num_words, 3);
                  IF NOT Is_Valid_Noun(w[3])
                  THEN
                    BEGIN
                      WriteLn('I don''t understand ', w[3],
                              ' as the object of a preposition.');
                      syntax_error := True;
                    END
                  ELSE
                    BEGIN
                      w[4] := w[3];
                      w[3] := w[2];
                      w[2] := w[4]; {object of prep = noun}
                      noun_adj := adj;
                      object_adj := adj;
                    END
                END
              ELSE
                IF ((w[1] = 'EXAMINE') AND Skip_A_Line) THEN
                  BEGIN
                    WriteLn('You see nothing unusual.');
                    syntax_error := True;
                  END
                ELSE BEGIN
                  WriteLn('I don''t understand ', w[2], ' as a noun.');
                  syntax_error := True;
                END
            ELSE IF num_words > 2 THEN {let's continue parsing}
              IF NOT Is_Prep(w[3])
              THEN
                BEGIN
                  WriteLn('I don''t understand ', w[3], ' as a preposition.');
                  syntax_error := True;
                  ReadLn;
                END
              ELSE                {make sure the last word is a noun, too}
                BEGIN
                  Massage_Noun(w, num_words, 4);
                  object_adj := adj;
                  IF NOT Is_Valid_Noun(w[4])
                  THEN IF w[4] <> '' THEN {allow missing obj. of prep}
                      BEGIN
                        WriteLn('I don''t understand ', w[4],
                                ' as the object of a preposition.');
                        syntax_error := True;
                      END;
                END;
          END;
        verb := w[1];
        noun := w[2];
        prep := w[3];
        object_word := w[4];
        IF (DoingCompoundCommands OR PreviousCompoundCommands) THEN
          IF ((prep = '') AND (Verb_Needs_Prep(verb))) THEN
            BEGIN                 {Complex Verbs like SHOOT MONSTER WITH LASER}
              Scan_For_Prep_Object(LastPart, NextPrep, NextObject, syntax_error);
              IF syntax_error THEN
                WriteLn('You need a preposition and object whenever you try to "', verb, ' ', noun, '"!')
              ELSE BEGIN
                prep := NextPrep;
                object_word := NextObject;
              END
            END;
        IF syntax_error THEN GOTO Exit;
        IF (object_word = 'IT') OR (object_word = 'THEM') {Allows 'IT' or 'THEM'}
        OR (object_word = 'HIM') OR (object_word = 'HER') {Allows 'HIM' or 'HER'}
        THEN BEGIN
          object_word := last_noun_used;
          object_adj := last_adj_used;
        END;
        IF (noun = 'IT') OR (noun = 'THEM')
        OR (noun = 'HIM') OR (noun = 'HER') {Allows 'HIM' or 'HER'}
        THEN BEGIN
          noun := last_noun_used;
          noun_adj := last_adj_used;
        END
        ELSE BEGIN
          last_noun_used := noun;
          last_adj_used := noun_adj;
        END;
        {check for duplicate names and pick one that is visible}
        Adjective := noun_adj;
        IF noun <> '' THEN NounNumber := Noun_Number(noun);
        IF Duplicate AND Skip_A_Line {I.E., two or more are visible}
        THEN HandleDuplicates(0); {0 for NounNumber selection}
        Adjective := object_adj;
        IF ((object_word <> '') AND (NOT syntax_error)) THEN ObjectNumber := Noun_Number(object_word);
        IF Duplicate AND Skip_A_Line {I.E., two or more are visible}
        THEN HandleDuplicates(1); {1 for Object selection}
      END;                        {if sentence=''...else}
    IF (NOT syntax_error) AND (noun <> '') THEN
      BEGIN                       {make sure we use the "official" form of noun}
        i := NounNumber;
        IF (i >= First_noun) AND (i <= MaxNoun)
        THEN noun := N[i]^.name;
        IF (i >= First_creature) AND (i <= MaxCreature)
        THEN noun := M[i]^.name;
      END;
    IF (NOT syntax_error) AND (object_word <> '') THEN
      BEGIN                       {make sure we use the "official" form of object}
        i := ObjectNumber;
        IF (i >= First_noun) AND (i <= MaxNoun)
        THEN object_word := N[i]^.name;
        IF (i >= First_creature) AND (i <= MaxCreature)
        THEN object_word := M[i]^.name;
      END;
Exit:
  END;                            {Parse(sentence)}

